//
// Created by zsj on 2019/12/21.
//

#include "arg_trans.h"
#include "../al/rsa_file_op.h"

bool __symmetric(const string& alg) {
    return alg == ALG_DES;
}

bool __asymmetric(const string& alg) {
    return alg == ALG_RSA;
}

string get_arg_val(int& i, int argc, char** argv, const char* name) {
    if (strlen(argv[i]) > strlen(name)) {
        i++;
        return string(argv[i - 1]).substr(strlen(name));
    } else {
        if (i == argc - 1) {
            __need_arg(string(argv[i]));
            return "";
        } else {
            i += 2;
            return string(argv[i - 1]);
        }
    }
}

void to_lower_case(string& raw) {
    for (int i = 0; i < raw.length(); i++) {
        if (raw[i] >= 'A' && raw[i] <= 'Z') {
            raw[i] = raw[i] - 'A' + 'a';
        }
    }
}

void check_arg(scli_options* options) {
    if (options->mode == 0) {
        __need_arg("-d/-e");
    } else {
        if (options->algname.empty()) {
            __need_arg("-a");
        }
        if (options->in.empty()) {
            __need_arg("-i");
        }
        if (options->out.empty()) {
            __need_arg("-o");
        }
        if ((options->mode & MODE_SIGNATURE) == 0) {
            if (__asymmetric(options->algname)) {
                if (options->key.empty()) {
                    __need_arg("-k");
                }
            } else {
                if (options->passphrase.empty()) {
                    __need_arg("-p");
                }
            }
        }
    }
}

void grade(scli_options* options) {
    if (options->mode == MODE_DECRYPT) {

        if (__symmetric(options->algname)) {
            options->algorithm->set_cipher(read_from_file(options->in));
            options->algorithm->set_key(options->passphrase);
            write_to_file(options->out, options->algorithm->execute(options->mode));
        } else {
            // 实验二 公钥加密文件

            write_to_file(options->out, asym_decrypt(read_from_file(options->key), read_from_file(options->in)));
        }
    } else if (options->mode == MODE_ENCRYPT) {

        if (__symmetric(options->algname)) {
            options->algorithm->set_plain(read_from_file(options->in));
            options->algorithm->set_key(options->passphrase);
            write_to_file(options->out, options->algorithm->execute(options->mode));
        } else {
            // 实验二 公钥加密文件

            write_to_file(options->out, asym_encrypt(read_from_file(options->key), read_from_file(options->in)));
        }
    } else if (options->mode & MODE_SIGNATURE) {
        string _pub_key;
        string _priv_key;

        if (options->mode & MODE_DECRYPT) {
            cout << "请选择用于解密的RSA私钥文件：";
            cin >> _priv_key;

            cout << "请选择用于验签的DSA公钥文件：";
            cin >> _pub_key;

            options->algorithm->set_cipher(read_from_file(options->in));
        } else if (options->mode & MODE_ENCRYPT) {
            cout << "请选择用于加密的RSA公钥文件：";
            cin >> _pub_key;

            cout << "请选择用于签名的DSA私钥文件：";
            cin >> _priv_key;

            options->algorithm->set_plain(read_from_file(options->in));
        }

        ((dgst*) options->algorithm)->set_pri(read_from_file(_priv_key));
        ((dgst*) options->algorithm)->set_pub(read_from_file(_pub_key));
        write_to_file(options->out, options->algorithm->execute(options->mode & (MODE_DECRYPT | MODE_ENCRYPT)));
    }
}

void handle(int argc, char** argv) {
    int i = 1;
    auto* options = new scli_options();
    while (i < argc) {
        // 取选项
        string arg = string(argv[i]);
        if (arg.find(ARG_ALGORITHM) == 0) {

            string val = get_arg_val(i, argc, argv, ARG_ALGORITHM);
            to_lower_case(val);
            if (val == "des") {
                options->algname = ALG_DES;
                options->algorithm = new des_crypto();
            } else if (val == "rsa") {
                options->algname = ALG_RSA;
                options->algorithm = new rsa_crypto();
            } else {
                __err("不支持的算法\"" + val + "\"");
            }

            // 数字签名使用RSA算法
            if (options->mode & MODE_SIGNATURE) {
                options->algname = ALG_RSA;
                options->algorithm = new rsa_crypto();
            }


        } else if (arg.find(ARG_DECRYPT) == 0) {

            if (options->mode & MODE_ENCRYPT) {
                __err("选项-d/-e不支持一起使用。");
            } else {
                options->mode |= MODE_DECRYPT;
                i++;
            }

        } else if (arg.find(ARG_ENCRYPT) == 0) {

            if (options->mode & MODE_DECRYPT) {
                __err("选项-d/-e不支持一起使用。");
            } else {
                options->mode |= MODE_ENCRYPT;
                i++;
            }

        } else if (arg.find(ARG_HELP) == 0) {

            usage();
            exit(0);

        } else if (arg.find(ARG_INFILE) == 0) {

            string val = get_arg_val(i, argc, argv, ARG_INFILE);
            options->in = val;

        } else if (arg.find(ARG_KEYFILE) == 0) {

            string val = get_arg_val(i, argc, argv, ARG_KEYFILE);
            options->key = val;

        } else if (arg.find(ARG_OUTFILE) == 0) {

            string val = get_arg_val(i, argc, argv, ARG_OUTFILE);
            options->out = val;

        } else if (arg.find(ARG_PASSPHRASE) == 0) {

            string val = get_arg_val(i, argc, argv, ARG_PASSPHRASE);
            options->passphrase = val;

        } else if (arg.find(ARG_SIGNATURE) == 0) {

            options->mode |= MODE_SIGNATURE;
            options->algname = ALG_RSA;
            options->algorithm = new dgst();
            i++;

        } else {
            __invalid_arg(arg);
        }
    }

    check_arg(options);

    grade(options);

    free(options);
}

void usage() {
    cout << __usage << endl;
}